using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;

using System.Timers;

namespace ClientGUI.Util
{
    /// <summary>
    /// The callback which is called repeatedly in an animation
    /// </summary>
    /// <param name="val">The current value in the animation</param>
    public delegate void AnimatorCallback(int val);


    /// <summary>
    /// This base class is an implentation using logarithmic animation
    /// </summary>
    public class Animator
    {
        int my_from_val;
        int my_to_val;
        int my_length;
        int my_log;
        int my_accuracy;
        AnimatorCallback my_callback;

        /// <summary>
        /// Constructs a linear animation
        /// </summary>
        /// <param name="from"></param>
        /// <param name="to"></param>
        /// <param name="milliseconds"></param>
        /// <param name="call"></param>
        public Animator(int from,
                        int to,
                        int milliseconds,
                        AnimatorCallback call)
        {
            my_from_val = from;
            my_to_val = to;
            my_length = milliseconds;
            my_callback = call;
            my_log = -1;
            my_accuracy = Math.Abs(to - from) / 10;
        }

        /// <summary>
        /// Constructs a logrithmic animation
        /// </summary>
        /// <param name="from"></param>
        /// <param name="to"></param>
        /// <param name="milliseconds"></param>
        /// <param name="call"></param>
        /// <param name="log"></param>
        public Animator(int from, 
                        int to, 
                        int milliseconds, 
                        AnimatorCallback call, 
                        int log)
        {
            my_from_val = from;
            my_to_val = to;
            my_length = milliseconds;
            my_callback = call;
            my_log = log;
            my_accuracy = log;
        }
        /// <summary>
        /// Animate, doesn't return until the animation is finished.
        /// </summary>
        public virtual void Animate()
        {
            int ops_needed;
            if (my_log > 0)
            {
                ops_needed = (int)Math.Log((double)Math.Abs(my_from_val - my_to_val), (double)my_log);
            }
            else
            {
                ops_needed = 10;
            }
            if (ops_needed > 1)
            {
                DateTime start_t = DateTime.Now;
                int wait = (my_length / ops_needed)/4; //give 3/4 time for actual display work.
                int move;
                if (my_log > 0)
                    move = (my_to_val - my_from_val) / my_log;
                else
                    move = (my_to_val - my_from_val) / ops_needed;
                if (move == 0) move = 1;
                int ticks = 0;
                for (int i = my_from_val; Math.Abs(my_to_val - i) >= my_accuracy; i += move)
                {
                    my_callback(i);
                    if (my_log > 0) //if its logarithmic
                        move = (my_to_val - i) / my_log;
                    else
                    { //if its linear.
                        if (Math.Abs(i + move - my_to_val) > Math.Abs(i - my_to_val))
                            break; //we're going the wrong way
                    }
                    Thread.Sleep(wait);              
                    ++ticks;

                    //adapting time to the cost of the process:
                    DateTime now = DateTime.Now;
                    if (ticks * wait > (now.Ticks - start_t.Ticks)/10000)
                    {
                        wait = wait / 2;
                    }
                    else if (ticks * wait < (now.Ticks - start_t.Ticks)/10000)
                    {
                        wait = wait * 2;
                    }
                }
            }
            my_callback(my_to_val);
        }
    }
}
